package com.katesoft.scale4j.agent;

import com.katesoft.scale4j.agent.args.AgentOptions;
import com.katesoft.scale4j.agent.jmx.ServiceManagerJMXExporter;
import com.katesoft.xml.jvmcluster.ApplicationConfigurationDocument.ApplicationConfiguration;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;

import java.util.concurrent.Callable;

import static com.katesoft.scale4j.common.lang.RuntimeUtility.isJunit;
import static com.katesoft.scale4j.log.LogFactory.getLogger;

/** @author kate2007 */
public class AgentLauncher implements Callable<ServiceLauncher>, DisposableBean {
   private AgentOptions options;
   private Thread launchThread;
   private Throwable exception;
   private ServiceLauncher launcher;
   private ClassPathXmlApplicationContext context;

   public AgentLauncher(AgentOptions options) {
      this.options = options;
   }

   @Override
   public ServiceLauncher call() throws Exception {
      context = new ClassPathXmlApplicationContext("META-INF/spring/jvmcluster$agent.xml");
      //
      ServiceManagerJMXExporter exporter = (ServiceManagerJMXExporter) context
               .getBean("jvmcluster.agent.service.manager.jmx.exporter");
      ApplicationConfigurationReader reader = (ApplicationConfigurationReader) context
               .getBean("jvmcluster.appConfiguration.reader");
      //
      ApplicationConfiguration configuration = (options.appConfigurationLocation() != null ? reader
               .parseConfiguration(options.appConfigurationLocation().getInputStream()) : reader
               .parseConfiguration(new ClassPathResource(Agent.DEFAULT_LOCATION).getInputStream()));
      // start services
      launcher = new ServiceLauncher(configuration, options);
      exporter.setLauncher(launcher);
      exporter.setContext(context);
      Thread hook = new Thread(new Runnable() {
         @Override
         public void run() {
            getLogger(Agent.class).info("JVM shutdown hook invoked to stop all services");
            try {
               launcher.stopAllServices(false);
            } finally {
               getLogger(Agent.class).info("destroying application context = %s",
                        context.getDisplayName());
               context.destroy();
            }
         }
      });
      hook.setName("scale4j.agent.shutdown.hook.thread");
      Runtime.getRuntime().addShutdownHook(hook);
      launchThread = new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               launcher.start();
            } catch (Throwable e) {
               exception = e;
               getLogger(AgentLauncher.class).error(e);
               if (!isJunit()) {
                  System.exit(-1);
               }
            }
         }
      });
      launchThread.setName("scale4j.agent.launch.thread");
      launchThread.start();
      return launcher;
   }

   public void waitFor() throws Throwable {
      launchThread.join();
      if (exception != null) {
         throw exception;
      }
   }

   @Override
   public void destroy() throws Exception {
      try {
         if (context != null) {
            context.destroy();
         }
      } finally {
         if (launcher != null) {
            launcher.stopAllServices(true);
         }
      }
   }
}
